#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#define SERVPORT 3333
#define MAXDATASIZE 100

int main(int argc,char *argv[]) {
    int sockfd, sendbytes;
    char buf[MAXDATASIZE];
    struct hostent* host;
    struct sockaddr_in serv_addr;
	char send_line[MAXDATASIZE], recv_line[MAXDATASIZE];
	
	// 定义检测可读文件描述符集合	
	fd_set rfds;
    fd_set readmask;
    int retval;

    if(argc < 2) {
        fprintf(stderr, "Please enter the server's hostname");
        exit(1);
    }

    if((host = gethostbyname(argv[1])) == NULL) { //转换为hostent
        perror("gethostbyname");
        exit(1);
    }
	
    if((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
        perror("创建 client socket 失败");
        exit(1);
    }
	
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERVPORT);
    serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
	
    if((connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr))) == -1) {
        perror("connect error");
        exit(1);
    }	
	
	// 将可读文件描述符集合中所有比特位设置为 0
	FD_ZERO(&rfds);
	// 设置需要检测文件描述符为 0 是否有数据可读
    FD_SET(0, &rfds);
	// 设置需要检测文件描述符为 sockfd 是否有数据可读
    FD_SET(sockfd, &rfds);
	
	for (;;) {
		// select 调用每次完成测试之后，内核都会修改描述符集合
		// 所以需要重新设置读文件描述符集合
		readmask = rfds;
		// 检测是否有关心的 I/O 事件发生，如果没有，则一直阻塞
        retval = select(sockfd + 1, &readmask, NULL, NULL, NULL);

        if (retval == -1)
            perror("select()");
		// 判断文件描述符为 0 是否有可读事件发生
		if (FD_ISSET(0, &readmask)) {
			if (fgets(send_line, MAXDATASIZE, stdin) != NULL) {
            	if (!strcmp(send_line, "quit\n")) {
                	break;
            	}
				int i = strlen(send_line);
            	if (send_line[i - 1] == '\n') {
                	send_line[i - 1] = 0;
            	}
            	printf("sending '%s'\n", send_line);
            	size_t rt = write(sockfd, send_line, strlen(send_line));
            	if (rt < 0) {
                	perror("write failed");
            	}
            	printf("sended bytes: %zu \n", rt);
        	}
		}
		// 判断文件描述符为 sockfd 是否有可读事件发生
		if (FD_ISSET(sockfd, &readmask)) {
			int n = read(sockfd, recv_line, MAXDATASIZE);
        	if (n < 0) {
            	perror("read failed");
        	} else if (n == 0) {
            	perror("server terminated \n");
        	}
        	recv_line[n] = 0;
        	fputs(recv_line, stdout);
        	fputs("\n", stdout);
		}
	}

    close(sockfd);

}
